#include <cstdio>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <esp_log.h>
#include <my_wifi.h>
#include <auto_json.h>
#include <my_webserver.h>
#include <my_spiffs.h>
#include <map>
#include <algorithm>
#include <my_wifi.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_wifi.h>
#include <esp_event.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include "UDPManager.h"
#include "uart.h"

constexpr const char *TAG = "Application";
#define NVS_NAMESPACE "wifi_infos"

using namespace app;

WebServer server(80, 3);
WiFi &wifi = WiFi::getInstance();
DNSServer dnsServer;

esp_err_t nvs_write_data(const std::string &ssid, const std::string &password)
{
    nvs_handle_t nvs_handle;
    esp_err_t err;

    // 打开NVS命名空间（NVS_READWRITE表示可读写）
    err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
    if (err != ESP_OK)
    {
        ESP_LOGE(TAG, "打开NVS失败: %s\n", esp_err_to_name(err));
        return err;
    }

    bool _s = nvs_set_str(nvs_handle, "ssid", ssid.c_str()) == ESP_OK &&
              nvs_set_str(nvs_handle, "password", password.c_str()) == ESP_OK;

    if (!_s)
    {
        ESP_LOGE(TAG, "写入ssid或password失败: %s\n", esp_err_to_name(err));
        nvs_close(nvs_handle);
        return err;
    }

    // 提交写入（必须调用，否则数据不会保存到闪存）
    err = nvs_commit(nvs_handle);
    if (err != ESP_OK)
    {
        ESP_LOGE(TAG, "提交数据失败: %s\n", esp_err_to_name(err));
        nvs_close(nvs_handle);
        return err;
    }

    ESP_LOGI(TAG, "数据写入成功\n");
    // 关闭NVS句柄
    nvs_close(nvs_handle);
    return ESP_OK;
}

std::string nvs_read_str(nvs_handle_t nvs_handle, const std::string &key)
{
    // 读取字符串（先获取长度，再分配缓冲区）
    size_t str_len;
    std::string ret = "";

    esp_err_t err = nvs_get_str(nvs_handle, key.c_str(), NULL, &str_len); // 第一次调用获取长度
    if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND)
    {
        ESP_LOGE(TAG, "获取字符串长度失败: %s\n", esp_err_to_name(err));
        return ret;
    }

    char *buf = (char *)malloc(str_len);
    if (!buf)
    {
        ESP_LOGE(TAG, "分配内存失败: %s\n", esp_err_to_name(err));
        return ret;
    }

    if (err == ESP_OK)
    {
        err = nvs_get_str(nvs_handle, key.c_str(), buf, &str_len);

        if (err == ESP_OK)
        {
            ret = buf;
        }
    }
    else
    {
        ESP_LOGE(TAG, "字符串键不存在\n");
    }
    free(buf);
    return ret;
}

// 从NVS读取数据
std::map<std::string, std::string> nvs_read_data()
{
    nvs_handle_t nvs_handle;
    esp_err_t err;

    // 打开NVS命名空间（NVS_READONLY表示只读）
    err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs_handle);
    if (err != ESP_OK)
    {
        ESP_LOGE(TAG, "打开NVS失败: %s\n", esp_err_to_name(err));
        return {};
    }
    std::map<std::string, std::string> ret;

    ret["ssid"] = nvs_read_str(nvs_handle, "ssid");
    ret["password"] = nvs_read_str(nvs_handle, "password");

    // 关闭NVS句柄
    nvs_close(nvs_handle);

    return ret;
}

// 从NVS删除数据
esp_err_t nvs_delete_data()
{
    nvs_handle_t nvs_handle;
    esp_err_t err;

    err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
    if (err != ESP_OK)
    {
        ESP_LOGE(TAG, "打开NVS失败: %s\n", esp_err_to_name(err));
        return err;
    }

    err = nvs_erase_key(nvs_handle, "ssid");
    if (err == ESP_OK)
    {
        ESP_LOGE(TAG, "删除ssid成功\n");
        nvs_commit(nvs_handle); // 提交删除操作
    }
    err = nvs_erase_key(nvs_handle, "password");
    if (err == ESP_OK)
    {
        ESP_LOGE(TAG, "删除password成功\n");
        nvs_commit(nvs_handle); // 提交删除操作
    }

    nvs_close(nvs_handle);
    return ESP_OK;
}

std::map<std::string, std::string> parseFormData(std::string &data)
{

    /*Remove invisible chars*/
    data.erase(
        std::remove_if(
            data.begin(),
            data.end(),
            [](unsigned char c)
            {
                return !(std::isprint(c) && c != ' ');
            }),
        data.end());

    std::map<std::string, std::string> result;

    size_t pos = 0;
    while ((pos = data.find("name=\"", pos)) != std::string::npos)
    {
        // 提取name
        size_t name_start = pos + 6; // "name=\"" 的长度是6
        size_t name_end = data.find("\"", name_start);
        std::string name = data.substr(name_start, name_end - name_start);

        size_t value_start = name_end + 1; /*jump the " */

        if (value_start != std::string::npos)
        {
            size_t value_end = data.find("-", value_start);
            std::string value = data.substr(value_start, value_end - value_start);
            result[name] = value;
        }

        pos = name_end;
    }

    return result;
}

void webServerInit()
{
    server.start();
    server.registUri("/wifi-list", HTTP_GET,
                     [](httpd_req_t *r)
                     {
                         auto wifi_list = wifi.getWiFiList();
                         // printf("%s\n", wifi_list.c_str());
                         server.send(r, wifi_list.c_str());
                         return ESP_OK;
                     });
    server.registUri("/save", HTTP_POST,
                     [](httpd_req_t *r)
                     {
                         auto body = server.getBody(r);
                         auto formData = parseFormData(body);

                         auto ssid = formData["ssid"];
                         auto password = formData["password"];

                         wifi.connect(ssid, password);

                         for (int i = 0; wifi.getStatus() != wifi_status_t::WIFI_CONNECTED && i < 10; i++)
                         {
                             vTaskDelay(1000 / portTICK_PERIOD_MS);
                         }
                         if (wifi.getStatus() == wifi_status_t::WIFI_CONNECTED)
                         {
                             nvs_write_data(ssid, password);
                             server.send(r, "sucess");
                             vTaskDelay(3000 / portTICK_PERIOD_MS);
                             esp_restart();
                         }
                         else
                         {
                             server.send(r, "fail");
                         }

                         return ESP_OK;
                     });
    server.registNotFound(HTTP_GET,
                          [](httpd_req_t *r)
                          {
                              FILE *f = SPIFFS.open("/wifi-config.html", "r");
                              if (f)
                              {
                                  server.send(r, f, getContentType("/wifi-config.html"));
                                  SPIFFS.closeFile(f);
                              }
                              else
                              {
                                  ESP_LOGW(TAG, "Failed to open /wifi-config.html");
                                  server.send(r, "Failed to open /wifi-config.html");
                              }
                              return ESP_OK;
                          });
    dnsServer.init();
}

// 写入数据到NVS

extern "C" void app_main(void)
{
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    bool system_start = false;

    SPIFFS.begin();
    wifi.start();
    wifi.onStatusChange(
        [&system_start](wifi_status_t status)
        {
            if (status == wifi_status_t::WIFI_CONNECTED)
            {
                ESP_LOGI(TAG, "wifi connected, ip:%s", wifi.getIP().c_str());
            }
            else if (status == wifi_status_t::WIFI_DISCONNECTED)
            {
                ESP_LOGI(TAG, "wifi disconnected");
                if (system_start)
                {
                    esp_restart();
                }
            }
        });

    wifi.setMode(WIFI_MODE_STA);

    auto wifi_infos = nvs_read_data();
    auto ssid = wifi_infos["ssid"];
    auto password = wifi_infos["password"];

    ESP_LOGW(TAG, "ssid:%s, password:%s", ssid.c_str(), password.c_str());

    wifi.connect(ssid, password);
    for (int cnt = 0; wifi.getStatus() != wifi_status_t::WIFI_CONNECTED && cnt < 30; cnt++)
    {
        vTaskDelay(500 / portTICK_PERIOD_MS);
        ESP_LOGI(TAG, "connecting ...");
    }

    if (wifi.getStatus() != wifi_status_t::WIFI_CONNECTED)
    {
        wifi.setMode(WIFI_MODE_APSTA);
        webServerInit();
        for (;;)
        {
            dnsServer.poll();
            vTaskDelay(pdMS_TO_TICKS(20));
        }
    }

    udpPort udp;

#if CONFIG_IDF_TARGET_ESP32C3
    uart uart(UART_NUM_1, GPIO_NUM_4, GPIO_NUM_5);
#else if CONFIG_IDF_TARGET_ESP32
    uart uart(UART_NUM_1, GPIO_NUM_12, GPIO_NUM_13);
#endif

    udp.start();
    uart.start();

    udp.onReceive(
        [&uart](const char *data, size_t len)
        {
             //ESP_LOGI(TAG, "UDP📥 收到消息: %.*s", len, data);
            uart.send(data, len);
        });
    uart.onReceive(
        [&udp](const char *data, size_t len)
        {
             //ESP_LOGI(TAG, "UART 📥 收到消息: %.*s", len, data);
            udp.send(data, len);
        });

    system_start = true;

    for (;;)
    {
        udp.send("__GET_PORT__");
        ESP_LOGI(TAG, "send __GET_PORT__");
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}